﻿using System.IO;
using NUnit.Framework;

namespace HappyTemplate.Tests
{
	[TestFixture]
	public class ExecutionTests : TestFixtureHelper
	{
		[Test]
		public void	WriteSimpleTemplate()
		{
			Template t = ParseTemplate("verbatim<$test$>mitaberv");

			_context["test"] = "hi";

			StringWriter sw = new StringWriter();
			t.Execute(_context, sw);

			Assert.AreEqual("verbatimhimitaberv", sw.ToString());

			AssertNoErrors();
		}

		[Test]
		public void WriteSimpleIfPair()
		{
			Template t = ParseTemplate("some<$if(trueValue)$>true<$endif if(falseValue)$>false<$endif$>ending");

			_context["trueValue"] = true;
			_context["falseValue"] = false;
	
			StringWriter sw = new StringWriter();
			t.Execute(_context, sw);

			Assert.AreEqual("sometrueending", sw.ToString());

			AssertNoErrors();
		}

		public void WriteSimpleIfFalse()
		{
			Template t = ParseTemplate("some<$if(falseValue)$>not<$endif$> value");

			_context["falseValue"] = false;

			StringWriter sw = new StringWriter();
			t.Execute(_context, sw);

			Assert.AreEqual("some false value", sw.ToString());

			AssertNoErrors();
		}

		[Test]
		public void WriteSimpleIfNotFalse()
		{
			Template t = ParseTemplate("some <$if(!falseValue)$>not<$endif$> false value");

			_context["falseValue"] = false;

			StringWriter sw = new StringWriter();
			t.Execute(_context, sw);

			Assert.AreEqual("some not false value", sw.ToString());

			AssertNoErrors();
		}

		[Test]
		public void WriteIfNested()
		{
			Template t = ParseTemplate("some<$if(trueValue)$>true<$if(!trueValue)$>not<$endif endif$>ending");

			_context["trueValue"] = true;
			
			StringWriter sw = new StringWriter();
			t.Execute(_context, sw);

			Assert.AreEqual("sometrueending", sw.ToString());

			AssertNoErrors();
		}

		[Test]
		public void	WriteIfElse()
		{
			Template t = ParseTemplate("some <$if(trueValue)$>true<$else$>false<$endif$> value");

			_context["trueValue"] = true;
			StringWriter sw = new StringWriter();
			t.Execute(_context, sw);
			Assert.AreEqual("some true value", sw.ToString());

			_context["trueValue"] = false;
			sw = new StringWriter();
			t.Execute(_context, sw);
			Assert.AreEqual("some false value", sw.ToString());


			AssertNoErrors();
		}

		[Test]
		public void WriteSimpleForEach()
		{
			Template t = ParseTemplate("<$foreach(i in array) i next$>");

			_context["array"] = new[] { "this ", "is ", "an ", "array" };

			StringWriter sw = new StringWriter();
			t.Execute(_context, sw);

			Assert.AreEqual("this is an array", sw.ToString());
		}


		[Test]
		public void WriteSimpleForEachWithBetween()
		{
			Template t = ParseTemplate("<$foreach(i in array between \" \") i next$>");

			_context["array"] = new[] { "this", "is", "an", "array" };

			StringWriter sw = new StringWriter();
			t.Execute(_context, sw);

			Assert.AreEqual("this is an array", sw.ToString());
		}

		[Test]
		public void WriteTemplateExpansion()
		{
			const string templateSetToParse = "callMe() <{hi there, I was called!}>" +
										   "testTemplate()<{<$callMe()$>}>";

			TemplateSet set = ParseTemplateSet(templateSetToParse);

			string written = HandledWriteTemplate(set.GetTemplate("testTemplate"));
			Assert.AreEqual("hi there, I was called!", written);
		}

		[Test]
		public void WriteTemplateExpansionWithLiteralArgument()
		{
			const string templateSetToParse = "callMe(a) <{hi there, the value of a is \"<$a$>\"}>" +
										   "testTemplate()<{<$callMe(\"hello, world!\")$>}>";

			TemplateSet set = ParseTemplateSet(templateSetToParse);

			string written = HandledWriteTemplate(set.GetTemplate("testTemplate")); 
			Assert.AreEqual("hi there, the value of a is \"hello, world!\"", written);
		}
		[Test]
		public void WriteTemplateExpansionWithVariableArgument()
		{
			const string templateSetToParse = "callMe(a) <{hi there, the value of a is \"<$a$>\"}>" +
										   "testTemplate()<{<$callMe(someGlobal)$>}>";

			TemplateSet set = ParseTemplateSet(templateSetToParse);

			set.Context["someGlobal"] = "hello, world!";

			string written = HandledWriteTemplate(set.GetTemplate("testTemplate")); 
			Assert.AreEqual("hi there, the value of a is \"hello, world!\"", written);
		}

		[Test]
		public void WriteTemplateExpansionWithTemplateArgument()
		{
			const string templateSetToParse = "hiWorld() <{hello, world!}>" +
											"callMe(a) <{hi there, the value of a is \"<$a$>\"}>" +
										   "testTemplate()<{<$callMe(hiWorld())$>}>";

			TemplateSet set = ParseTemplateSet(templateSetToParse);

			string written = HandledWriteTemplate(set.GetTemplate("testTemplate"));
			Assert.AreEqual("hi there, the value of a is \"hello, world!\"", written);
		}

		[Test]
		public void WriteTemplateExpansionWithMultipleMixedArguments()
		{
			const string templateSetToParse = "hi(there) <{hi <$there$>, }>" +
											"theValueIs(value) <{the value is \"<$value$>\"}>" +
											"callMe(a, b, c)<{<$ a b c $>}>" +
										   "testTemplate()<{<$callMe(hi(\"there\"), theValueIs(hiWorld), \"!\")$>}>";

			TemplateSet set = ParseTemplateSet(templateSetToParse);
			set.Context["hiWorld"] = "hello, world";

			string written = HandledWriteTemplate(set.GetTemplate("testTemplate"));
			Assert.AreEqual("hi there, the value is \"hello, world\"!", written);
		}

		[Test]
		public void WriteLookup()
		{
			const string templateSetToParse = @"
lookup someLookup
{
	""inValue1"" = ""outValue1"",
	""inValue2"" = ""outValue2"",	
	""inValue3"" = ""outValue3"",
	default = ""defaultValue""
}
testTemplate()<{<$someLookup[""inValue1""] "" "" someLookup[""inValue2""] "" "" someLookup[""inValue3""] "" "" someLookup[""invalidValue""]	$>}>";

			TemplateSet set = ParseTemplateSet(templateSetToParse);

			string written = HandledWriteTemplate(set.GetTemplate("testTemplate"));
			Assert.AreEqual("outValue1 outValue2 outValue3 defaultValue", written);
		}


		[Test]
		public void WriteMultipartIdentiferExpansion()
		{
			Template t = ParseTemplate("<$test1.test2.test3$>");

			TemplateObject tobj1 = new TemplateObject();
			TemplateObject tobj2 = new TemplateObject();

			tobj1["test2"] = tobj2;
			tobj2["test3"] = "Hello world!";
			_context["test1"] = tobj1;

			string written = HandledWriteTemplate(t);
			Assert.AreEqual("Hello world!", written);
		}

		/// <summary>
		/// Ensures that the parameters of the caller are accessible to the callee.
		/// </summary>
		[Test]
		public void TestScope()
		{
			const string templateSetToParse = @"
callee()<{<$parentParam$>}>
caller(parentParam)<{<$ callee() $>}>
testTemplate()<{<$caller(""caller parameter"")$>}>
";
			TemplateSet set = ParseTemplateSet(templateSetToParse);

			string written = HandledWriteTemplate(set.GetTemplate("testTemplate"));
			Assert.AreEqual("caller parameter", written);
		}

		[Test]
		public void TestNullVariableHandling()
		{
			Template t = ParseTemplate("<$aNullValue$>");
			_context["aNullValue"] = null;
			string written = HandledWriteTemplate(t);
			Assert.AreEqual("", written);
		}

		/// <summary>
		/// Tests handling when a property of a TemplateObject value is null
		/// </summary>
		[Test]
		public void TestNullTemplateObjectProperty()
		{
			Template t = ParseTemplate("<$objectA.NullProperty objectA.NullProperty.SomeProperty $>");
			TemplateObject templateObject = new TemplateObject();
			templateObject["NullProperty"] = null;
			_context["objectA"] = templateObject;	

			string written = HandledWriteTemplate(t);
			Assert.AreEqual("", written);
		}

		private class TestClassA
		{
			public TestClassB NullProperty { get { return null; }}	
			public TestClassB NonNullProperty { get { return new TestClassB() ;}}
		}

		private class TestClassB
		{
			public string NullProperty { get { return null; } }
		}

		/// <summary>
		/// Tests handling when a property that is a scalar value is null
		/// </summary>
		[Test]
		public void TestNullObjectProperty1()
		{
			Template t = ParseTemplate("<$objectA.NullProperty$>");
			_context["objectA"] = new TestClassA();
			string written = HandledWriteTemplate(t);
			Assert.AreEqual("", written);
		}

		/// <summary>
		/// Tests handling when a property that is an object is null
		/// </summary>
		[Test]
		public void TestNullObjectProperty2()
		{
			Template t = ParseTemplate("<$objectA.NullProperty.NullProperty$>");
			_context["objectA"] = new TestClassA();
			string written = HandledWriteTemplate(t);
			Assert.AreEqual("", written);
		}

		/// <summary>
		/// Tests handling when a property that is an object is null
		/// </summary>
		[Test]
		public void TestNullObjectProperty3()
		{
			Template t = ParseTemplate("<$objectA.NonNullProperty.NullProperty$>");
			_context["objectA"] = new TestClassA();
			string written = HandledWriteTemplate(t);
			Assert.AreEqual("", written);	
		}

		[Test]
		public void TestSetStatement()
		{
			Template t = ParseTemplate(@"<$set aVariable = ""aValue"" aVariable$>");
			string written = HandledWriteTemplate(t);
			Assert.AreEqual("aValue", written);
			Assert.AreEqual(false, _context.ExistsInStack("aVariable"));
		}

		[Test]
		public void TestGlobalStatement()
		{
			Template t = ParseTemplate(@"<$global aVariable = ""aValue"" aVariable$>");
			string written = HandledWriteTemplate(t);
			Assert.AreEqual("aValue", written);
			Assert.AreEqual("aValue", _context["aVariable"].ToString());
		}

		[Test]
		public void TestSetStatement2()
		{
			TemplateSet ts = ParseTemplateSet(
@"
testTemplate() <{test template}>

main()
<{<$
	set var1 = someVariable
	set var2 = testTemplate()
	var1 ""|""
	var2
$>}>
");
			_context["someVariable"] = "Some Value";
			string written = HandledWriteTemplate(ts.GetTemplate("main"));
			Assert.AreEqual("Some Value|test template", written);
		}

		[Test]
		public void	TestNull()
		{
			TemplateSet ts = ParseTemplateSet(
@"
testTemplate() <{test template}>

main()
<{<$
	if(isNull(var1))
		""null""
	else
		""not null""
	endif

	global var1 = null

	""|""

	if(isNull(var1))
		""null""
	else
		""not null""
	endif
$>}>
");
			_context["var1"] = "Some Value";
			Assert.IsNotNull(_context["var1"]);
			string written = HandledWriteTemplate(ts.GetTemplate("main"));
			Assert.AreEqual("not null|null", written);
			Assert.IsNull(_context["var1"]);
		}

		[Test]
		public void	TestAnonymousTemplate()
		{
			const string actual = @"<$
set anon = ""anonymous""
set output = <{I am <$anon$>.}>
output
$>";
			Assert.AreEqual("I am anonymous.", HandledWriteTemplate(actual));
		}

		[Test]
		public void TestCase1()
		{
			TemplateSet ts = ParseTemplateSet(
@"
testTemplate() <{test template}>

main()
<{<$
set list = newList(""a"", ""b"", ""c"", ""d"", ""e"", ""f"")

foreach(item in list between "" "")
	switch(item)
		case ""a"": 1
		case ""b"": 2 "" "" 
		case ""c"", ""d"": 3
			default:
	endswitch
next
$>}>
");
			_context["var1"] = "Some Value";
			Assert.IsNotNull(_context["var1"]);
			string written = HandledWriteTemplate(ts.GetTemplate("main"));
			Assert.AreEqual("not null|null", written);
			Assert.IsNull(_context["var1"]);
		}
	}
}

